#include "type.h"
#include "token.h"
#include "symbol.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

smap *types;

symtype* symtype_create( syntype ty, void *attr, int size )
{
    symtype *st = (symtype *)malloc( sizeof(symtype) );
    init_life( st );
    st->syn = ty;
    st->attr = attr;
    st->size = size;
    return st;
}

int symtype_release( symtype *ty )
{
    int ref;
    ref = release( ty );
    if( ref <= 0 )
    {
        if( ty->attr )
        {
            macrotype *mt;
            vartype *vt;
            functype *ft;
            structtype *st;
            enumtype *et;
            uniontype *ut;
            switch( ty->syn )
            {
            case T_MACRO:
                mt = (macrotype *)ty->attr;
                macro_release( mt );
                break;
            case T_VAR:
                vt = (vartype *)ty->attr;
                vt_release( vt );
                break;
            case T_FUNC:
                break;
            case T_STRUCT:
                break;
            case T_ENUM:
                break;
            case T_UNION:
                break;
            }
            free( ty->attr );
        }
    }
    return ref;
}

vartype* vt_create( base_type ty, void *base, int ext )
{
    vartype *vt = (vartype *)malloc( sizeof(vartype) );
    init_life( vt );
    vt->type = ty;
    vt->name = base;
    vt->ext = ext;
    switch( ty )
    {
    case T_SIMPLE:
        vt->size = ((symbol*)base)->type->size;
        add_ref( base );
        break;
    case T_POINT:
        vt->size = 4;
        add_ref( ((pointtype*)base)->base );
        break;
    case T_ARRAY:
        vt->size = ((arrtype*)base)->base->size * vt_size( ((arrtype*)base)->count->ret );
        add_ref( ((arrtype*)base)->base );
        break;
    }
    sm_insert( types, vt_name(vt), vt );
    add_ref( vt );
    return vt;
}

int vt_release( vartype *ty )
{
    int ref;
    ref = release( ty );
    if( ref <= 0 )
    {
        switch( ty->type )
        {
        case T_SIMPLE:
            sym_release( (symbol*)ty->name );
            break;
        case T_POINT:
            vt_release( ((pointtype*)ty->name)->base );
            free( ty->name );
            break;
        case T_ARRAY:
            vt_release( ((arrtype*)ty->name)->base );
            exp_tree_release( ((arrtype*)ty->name)->count );
            free( ty->name );
            break;
        }
        free( ty );
    }
    return ref;
}

long vt_size( vartype *ty )
{
    return ty->size;
}

vartype* point_create( vartype *base, int ext )
{
    pointtype *pt;
    pt = (pointtype *)malloc( sizeof(pointtype) );
    pt->base = base;
    return vt_create( T_POINT, pt, ext );
}

vartype* array_create( vartype *base, exp_tree *count, int ext )
{
    arrtype *at;
    at = (arrtype *)malloc( sizeof(arrtype) );
    at->base = base;
    at->count = count;
    add_ref( count );
    return vt_create( T_ARRAY, at, ext );
}

char* vt_name( const vartype *vt )
{
    static const char* m[11] = {
        "extern ", "static ", "const ", "auto ", "register ",
        "restrict ", "volatile ", "unsigned ", "long ", "typedef",
        "inline"
    };
    static char buf[10240];
    char *name;
    int i;

    switch( vt->type )
    {
    case T_SIMPLE:
        buf[0] = 0;
        for( i = 0; i < 9; ++i )
        {
            if( vt->ext & (1<<i) )
                strcat( buf, m[i] );
        }
        name = sym_name( (symbol*)vt->name, 0 );
        strcat( buf, name );
        free( name );
        name = (char *)malloc( strlen(buf) + 1 );
        strcpy( name, buf );
        break;
    case T_POINT:
        name = vt_name( (vartype*)((pointtype*)vt->name)->base );
        strcpy( buf, name );
        strcat( buf, "*" );
        for( i = 0; i < 9; ++i )
        {
            if( vt->ext & (1<<i) )
                strcat( buf, m[i] );
        }
        free( name );
        name = (char *)malloc( strlen(buf) + 1 );
        strcpy( name, buf );
        break;
    case T_ARRAY:
        name = vt_name( (vartype*)((arrtype*)vt->name)->base );
        strcpy( buf, name );
        strcat( buf, " " );
        for( i = 0; i < 9; ++i )
        {
            if( vt->ext & (1<<i) )
                strcat( buf, m[i] );
        }
        sprintf( buf + strlen(buf), "[%d]", ((arrtype*)vt->name)->count );
        free( name );
        name = (char *)malloc( strlen(buf) + 1 );
        strcpy( name, buf );
        break;
    }
    return name;
}

macrotype* macro_create( token *name, list *para, list *rep )
{
    token *tk, *t;
    macrotype *mt;
    int j, k;
    if( para )
    {
        for( j = 0; j < lt_size(rep); ++j )
        {
            t = (token *)lt_get( rep, j );
            if( 0 == strcmp( t->name, "__VA_ARGS__" ) )
            {
                t = (token *)lt_update( rep, j, (void *)-1 );
                token_release( t );
            }
            else
            {
                for( k = 0; k < lt_size(para); ++k )
                {
                    tk = (token *)lt_get( para, k );
                    if( tkcmp( t, tk ) == 0 )
                    {
                        tk = (token *)lt_update( rep, j, (void*)k );
                        token_release( tk );
                        break;
                    }
                }
            }
        }
    }
    mt = (macrotype *)malloc( sizeof(macrotype) );
    mt->name = name;
    mt->para = para;
    if( para )
        mt->npara = lt_size( para );
    else
        mt->npara = 0;
    mt->rep = rep;
    add_ref( name );
    return mt;
}

int macro_release( macrotype *mt )
{
    int i;
    token *tk;
    for( i = 0; i < lt_size(mt->rep); ++i )
    {
        tk = (token *)lt_get( mt->rep, i );
        if( (int)tk >= 10000 )
            token_release( tk );
    }
    for( i = 0; i < lt_size( mt->para ); ++i )
        token_release( (token *)lt_get( mt->para, i ) );
    token_release( mt->name );
    lt_destroy( mt->para );
    lt_destroy( mt->rep );
    free( mt );
    return 0;
}

void macro_replace( macrotype *mac, list *para, list *tokens )
{
    token *tk;
    int i;
    if( mac->npara == 0 )
    {
        for( i = 0; i < lt_size( mac->rep ); ++i )
        {
            tk = (token *)lt_get( mac->rep, i );
            lt_pushb( tokens, tk );
            add_ref( tk );
        }
    }
    else
    {
        for( i = 0; i < lt_size( mac->rep ); ++i )
        {
            tk = (token *)lt_get( mac->rep, i );
            if( (int)tk < 10000 )
            {
                if( (int)tk >= 0 )
                {
                    tk = (token *)lt_get( para, (int)tk );
                    if( tk )
                    {
                        lt_pushb( tokens, tk );
                        add_ref( tk );
                    }
                }
                else
                {
                    int j;
                    for( j = mac->npara; j < lt_size( para ); ++j )
                    {
                        tk = (token *)lt_get( para, j );
                        lt_pushb( tokens, tk );
                        add_ref( tk );
                    }
                }
            }
            else
            {
                lt_pushb( tokens, tk );
                add_ref( tk );
            }
        }
    }
}
